import { type IUploadFilesResData, type IUploadImgFilesResData } from '@/api/indexTypes';
import { defHttp } from '@/utils/http';
import { Api as RootApi } from '@/api';
import { computed, isRef, type Ref, ref, unref } from 'vue';
import { cloneDeep } from 'lodash-es';
import { isNumber } from '@/utils/is';
import { useDebounceFn } from '@vueuse/core';
import { Toast } from '@/utils';
import { navigateTo } from '@/router';
import { type CropImg } from '@/router/types/modules';
import { logManager } from '@/utils/log';

const chooseImgItemList = [
  '相机',
  '从手机相册选择',
  // #ifdef MP-WEIXIN
  '从聊天记录中选择',
  // #endif
]

export interface DocFileResult {
  name: string;
  url: string;
  size: number;
  fileType: string;
}


export interface UseUploadOptions {
  /** 最大数 */
  count?: number | Ref<number>;
  // 文件大小 M
  fileSize?: number;
  // 自定义上传时机
  customTriggerUpload?: boolean;
  // 是否上传视频封面 (注: 仅video类型下生效)
  isUploadVideoCover?: boolean;
  /** 是否裁剪图片 */
  isCrop?: boolean;
  /** 裁剪相关配置 */
  cropOptions?: CropImg;
}

export type MediaType = 'image' | 'video' | 'doc';

/** 文件默认大小 */
export const fileDefaultSize: Record<MediaType, number> = {
  image: 10,
  video: 50,
  doc: 50,
};

const maxCount = 9;

export const documentFileType = ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf'] as const;
export type DocumentFileType = typeof documentFileType[number];
export interface ResultItem {
  /** 文件名称 */
  fileName?: string;
  /** 文件临时地址 */
  tempFilePath?: string;
  /** 上传进度 */
  progress: number;
  /** 文件大小 */
  fileSize?: number;
  /** 文件网络地址 */
  filePath?: string;
  /** 文件后缀 */
  extension?: string;
  /** 视频封面临时地址 */
  thumbTempFilePath?: string;
  /** 视频封面网络地址 */
  thumbFilePath?: string;
  /** 视频时长 */
  duration?: number;
  isErr?: boolean;
  isSuccess?: boolean;
}

export function useUpload(op?: UseUploadOptions) {
  const count = op && isRef(op?.count) ? op.count : ref(op?.count);
  const mediaType = ref<MediaType>('image');
  const result = ref<ResultItem[]>([]);
  const successResult = ref<ResultItem[]>([]);
  const fs = uni.getFileSystemManager()
  const basePath = wx.env.USER_DATA_PATH
  const downloadList = ref<{ name: string, tempFilePath: string, path: string }[]>([])

  /**
   * @description 清空文件缓存目录
   * @date 12/11/2024
   */
  function clearFileCacheDirectory() {
    downloadList.value = []
    const files = fs.readdirSync(basePath)
    for (let index = 0; index < files.length; index++) {
      const filePath = `${basePath}/${files[index]}`
      try {
        if (files[index] !== 'miniprogramLog') {
          fs.access({
            path: filePath,
            success: () => fs.unlinkSync(filePath)
          })
        }
      } catch (error) { console.log(error) }
    }
  }

  clearFileCacheDirectory()


  /**
   * @description 拍摄或从手机相册中选择图片或视频。
   * @date 02/11/2022
   * @param {UniNamespace.ChooseMediaOption} options
   */
  function chooseMedia(options: UniNamespace.ChooseMediaOption) {
    if (isMaxCount.value) return;
    uni.chooseMedia({
      sizeType: ['original', 'compressed'],
      count: unref(getBalance),
      ...options,
      success: (result) => {
        if (options.mediaType?.[0] === 'image' && op?.isCrop && unref(count) === 1) {
          navigateTo({
            name: 'cropImg', options: {
              ...op.cropOptions,
              imageUrl: result.tempFiles[0].tempFilePath,
              onSuccess: (res) => {
                const r = cloneDeep(result)
                r.tempFiles[0].tempFilePath = res.url;
                r.tempFiles[0].width = res.width;
                r.tempFiles[0].height = res.height;
                chooseMediaSuccess(r);
              },
            }
          });
        } else {
          chooseMediaSuccess(result);
        }
      },
      fail: (err) => {
        console.error('chooseMedia ===>', err);
        logManager.error(err)
      },
    });
  }

  /**
   * @description 验证是否需要上传视频封面
   * @date 02/11/2022
   * @returns {boolean}
   */
  function isUploadVideoCover(): boolean {
    return !!(mediaType.value === 'video' && op?.isUploadVideoCover);
  }

  /**
   * @description chooseMedia成功回调
   * @date 02/11/2022
   * @param {UniNamespace.ChooseMediaSuccessCallbackResult} mediaResult
   */
  function chooseMediaSuccess(mediaResult: UniNamespace.ChooseMediaSuccessCallbackResult) {
    mediaType.value = mediaResult.type as MediaType;
    mediaResult.tempFiles.forEach((v) => {
      const fileName = getFileName(v.tempFilePath);
      const item: ResultItem = {
        tempFilePath: v.tempFilePath,
        progress: 0,
        fileSize: v.size,
        fileName: fileName,
        extension: getFileExtension(fileName),
      };
      if (isUploadVideoCover()) {
        item.thumbTempFilePath = v.thumbTempFilePath;
        item.duration = v.duration;
      }
      verifyFileSize(v.size) && result.value.push(item);
    });
    !op?.customTriggerUpload && upload();
  }

  /**
   * @description 选择会话文件
   * @date 02/11/2022
   * @param {({ type: 'video' | 'image' | 'file' } & Partial<UniNamespace.ChooseMessageFileOption>)} options
   */
  function chooseMessageFile(
    options: { type: 'video' | 'image' | 'file' } & Partial<UniNamespace.ChooseMessageFileOption>,
  ) {
    if (isMaxCount.value) return;
    uni.chooseMessageFile({
      count: unref(getBalance),
      type: options.type,
      extension: options.extension || (documentFileType as unknown as string[]),
      success: (res) => {
        mediaType.value = options.type === 'file' ? 'doc' : options.type;
        res.tempFiles.forEach((v) => {
          const item: ResultItem = {
            tempFilePath: v.path,
            progress: 0,
            fileSize: v.size,
            fileName: v.name.split('.')[0],
            extension: getFileExtension(v.name),
          };
          verifyFileSize(v.size) && result.value.push(item);
        });
        !op?.customTriggerUpload && upload();
      },
      fail: (err) => {
        console.error('chooseMessageFile ===>', err)
        logManager.error(err)
      },
    });
  }

  /**
   * @description 获取文件后缀
   * @date 02/11/2022
   * @param {string} fileName 文件名
   * @returns {string}
   */
  function getFileExtension(fileName: string): string {
    return fileName.split('.').pop()?.toLowerCase() as string
  }

  /**
   * @description 获取文件名
   * @date 02/11/2022
   * @param {string} path 文件路径
   * @returns {string}
   */
  function getFileName(path: string): string {
    let fileName = path.split('//')[1];
    if (fileName.includes('tmp')) fileName.split('_')[1];
    return fileName;
  }

  /**
   * @description 验证文件大小是否合格
   * @date 02/11/2022
   * @param {number} fileSize 文件大小 B
   * @returns {boolean}
   */
  function verifyFileSize(fileSize: number): boolean {
    const size = op?.fileSize || fileDefaultSize[mediaType.value]
    const is = size * 1024 * 1024 > fileSize
    !is && Toast(`请上传${size}MB内的文件`)
    return is
  }

  /**
   * @description 判断上传数是否达到最大值
   * @date 02/11/2022
   * @returns {boolean}
   */
  const isMaxCount = computed(() => count.value === result.value.length);

  /**
   * @description 获取剩余可上传数量, 不传count默认为无限
   * @date 02/11/2022
   * @returns {number}
   */
  const getBalance = computed(() => {
    if (!count.value) return maxCount;
    const balance = count.value - result.value.length;
    return balance > maxCount ? maxCount : balance;
  });

  const uploadtIndex = ref(result.value.length > 0 ? result.value.length : 0);
  /**
   * @description 上传文件
   * @date 02/11/2022
   */
  async function upload(resetIndex?: number) {
    const index = isNumber(resetIndex) ? resetIndex : uploadtIndex.value;
    const item = result.value[index];
    if (uploadtIndex.value >= result.value.length && !isNumber(resetIndex)) return;
    if (!isNumber(resetIndex)) {
      uploadtIndex.value++;
    }
    if (!item || item.filePath || item.progress === 100) {
      !isNumber(resetIndex) && upload();
      return;
    }
    try {
      const resp = await request(mediaType.value, item);
      if (isUploadVideoCover()) {
        const { imagePathBig } = await request('image', item, true);
        item.thumbFilePath = imagePathBig;
      }
      item.filePath = resp.imagePathBig || resp.fileUrl;
      item.isSuccess = true;
      item.isErr = false;
      item.progress = 100; // 解决部分出现未赋值情况bug
      if (!isNumber(resetIndex)) successResult.value.push(cloneDeep(item));
    } catch (error) {
      item.isErr = true;
    } finally {
      upload();
    }
  }

  /**
   * @description 请求
   * @date 02/11/2022
   * @param {MediaType} type 媒体类型
   * @param {ResultItem} item 上传的数据项
   * @param {boolean} [isCover] 是否是封面
   * @returns {(Promise<IUploadImgFilesResData & IUploadFilesResData>)}
   */
  async function request(
    type: MediaType,
    item: ResultItem,
    isCover?: boolean,
  ): Promise<IUploadImgFilesResData & IUploadFilesResData> {
    const path =
      isCover && item.thumbTempFilePath ? item.thumbTempFilePath : (item.tempFilePath as string);
    const size = isCover ? 0 : item.fileSize || 0;
    return await defHttp.files(
      {
        url: type === 'image' ? RootApi.imgUpload : RootApi.fileUpload,
        files: {
          path,
          size,
        },
      },
      {
        onProgressUpdate({ progress }) {
          if (item.progress !== 100 && !isCover) {
            item.progress = progress;
          }
        },
      },
    );
  }

  /** 删除指定项 */
  function delResultItem(index: number) {
    if (result.value[index] && uploadtIndex.value > 0) uploadtIndex.value--;
    result.value.splice(index, 1);
  }

  /** 清空result数组 */
  function resetResult() {
    result.value = [];
  }

  /** 添加一项 */
  function addResultItem(item: ResultItem) {
    if (item.filePath && !hasResultItem(item.filePath)) {
      result.value.push(item);
    }
  }

  /**
   * @description 根据filePath查询子项是否存在
   * @date 04/11/2022
   * @param {string} filePath
   * @returns {boolean}
   */
  function hasResultItem(filePath: string): boolean {
    const val = result.value.find((v) => v.filePath === filePath);
    return !!val;
  }

  /** 重新上传 */
  function reupload(index: number) {
    upload(index);
  }

  /**
   * @description 选择文件
   * @date 04/11/2022
   * @param {{ mediaType: MediaType }} options
   */
  function chooseFile(options: { mediaType: MediaType }) {
    mediaType.value = options.mediaType;
    if (isMaxCount.value) return;
    if (options.mediaType === 'doc') {
      chooseMessageFile({
        type: 'file',
      });
    } else {
      showActionSheet();
    }
  }

  /**
   * @description 选择视频与图片的菜单
   * @date 04/11/2022
   */
  function showActionSheet() {
    const type = mediaType.value;
    if (type === 'doc') return;
    uni.showActionSheet({
      itemList: chooseImgItemList,
      success: ({ tapIndex }) => {
        switch (tapIndex) {
          case 0:
            chooseMedia({
              sourceType: ['camera'],
              mediaType: [type],
            });
            break;
          case 1:
            chooseMedia({
              sourceType: ['album'],
              mediaType: [type],
            });
            break;
          case 2:
            chooseMessageFile({
              type: type,
            });
            break;
        }
      },
    });
  }

  /**
   * @description 编辑下载文件的文件名
   * @date 12/05/2023
   * @param {string} filePath
   * @param {string} fileName
   * @param {DocumentFileType} fileType
   */
  function editFileName(filePath: string, fileName: string, fileType: DocumentFileType): Promise<string> {
    return new Promise((resolve, rejecte) => {
      const newPath = `${basePath}/${fileName}.${fileType}`
      fs.saveFile({
        tempFilePath: filePath,
        filePath: newPath,
        success: (res) => resolve(res.savedFilePath),
        fail: async (err) => {
          console.log('EditFileName Error', err, filePath);
          if (err.errMsg.includes('limit is exceeded')) {
            clearFileCacheDirectory()
            editFileName(filePath, fileName, fileType).then(resolve).catch(rejecte)
          } else {
            rejecte(err.errMsg)
          }
        }
      })
    })
  }

  const openDocument = useDebounceFn(
    (options: { filePath: string; fileType?: DocumentFileType, customFileName?: string }) => {
      const fileType =
        options.fileType ||
        (options.filePath.substring(options.filePath.lastIndexOf('.') + 1) as DocumentFileType);
      if (!fileType || !documentFileType.includes(fileType)) {
        Toast('文件格式错误')
        throw new Error(`文件格式错误${fileType}`);
      }

      /** 避免同一页面 重复下载 */
      const tempItem = downloadList.value.find(v => v.path === options.filePath)
      if (tempItem && tempItem.tempFilePath) {
        open(tempItem.tempFilePath)
        return
      }

      if (options.filePath.includes('tmp')) {
        open(options.filePath)
        return
      }

      async function open(tempFilePath: string) {
        let path: string = ''
        if (!tempItem && options.customFileName) {
          path = await editFileName(tempFilePath, options.customFileName, fileType)
        }
        if (!tempItem) {
          downloadList.value.push({ name: options.customFileName || '', tempFilePath: path || tempFilePath, path: options.filePath })
        }
        uni.openDocument({
          filePath: path || tempFilePath,
          fileType,
          showMenu: true,
          success: (result) => {
            console.log('文件预览成功', result)
          },
          fail: (err) => {
            Toast('文件预览失败');
            console.log(err, tempFilePath);
            logManager.error(err)
          },
          // complete: () => {
          //   setTimeout(() => fs.unlink({ filePath: tempFilePath }), 3000)
          // }
        } as UniApp.OpenDocumentOptions);
      }

      defHttp
        .download(options.filePath)
        .then((r) => {
          open(r)
        })
        .catch((err) => {
          console.log(err)
          Toast('文件下载失败')
        });
    },
    1000,
  );

  return {
    result,
    successResult,
    chooseMedia,
    chooseMessageFile,
    delResultItem,
    addResultItem,
    reupload,
    chooseFile,
    openDocument,
    request,
    resetResult
  };
}
